#include <zlib.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <png.h>
#include "defs.h"
#include  <stddef.h>
#include <stdint.h>
#include "memory.h"
#include <assert.h>

#define SIZE_CHUNK 16384
typedef unsigned char       UINT8, *PUINT8;
typedef unsigned int        UINT32, *PUINT32;
typedef unsigned char       BYTE;
typedef struct SpiceChunk {
	BYTE* data;
	UINT32 len;
} SpiceChunk;

typedef struct SpiceChunks {
	UINT32     data_size;
	UINT32     num_chunks;
	UINT32     flags;
	SpiceChunk*   chunk;
} SpiceChunks;


#define RED_COMPRESS_BUF_SIZE (1024 * 64)
struct RedCompressBuf {
	struct RedCompressBuf* send_next;

	/* This buffer provide space for compression algorithms.
	 * Some algorithms access the buffer as an array of 32 bit words
	 * so is defined to make sure is always aligned that way.
	 */
	union {
		UINT8  bytes[RED_COMPRESS_BUF_SIZE];
		UINT32 words[RED_COMPRESS_BUF_SIZE / 4];
	} buf;
};

typedef struct {
	struct RedCompressBuf* bufs_head;
	struct RedCompressBuf* bufs_tail;
	jmp_buf jmp_env;
	union {
		struct {
			SpiceChunks* chunks;
			int next;
			int stride;
			int reverse;
		} lines_data;
		struct {
			struct RedCompressBuf* next;
			int size_left;
		} compressed_data; // for encoding data that was already compressed by another method
	} u;
} EncoderData;





// int zlibEncode(){
  
// 	encoder_data_init(&zlib_data->data);

// 	zlib_data->data.u.compressed_data.next = lz_data->data.bufs_head;
// 	zlib_data->data.u.compressed_data.size_left = lz_size;

// 	zlib_size = zlib->encode(zlib_level,
// 		lz_size, zlib_data->data.bufs_head->buf.bytes,
// 		sizeof(zlib_data->data.bufs_head->buf));
// }
static void __do_deflate_write(z_stream * strm, 
        FILE *fp, void *data, size_t size, int flush)
{
    char out[SIZE_CHUNK];

    strm->avail_in = size;
    strm->next_in = (Bytef *)data;
    printf("deflate: %u\n", strm->avail_in);

    /*  run deflate() on input until output buffer not full, finish
     *  compression if all of source has been read in */
    do {
        strm->next_out = (Bytef *)out;
        strm->avail_out = sizeof(out);

        int res = deflate(strm, flush);
        assert(res != Z_STREAM_ERROR);  // state not clobbered

        size_t wlen = fwrite(out, 1, sizeof(out) - strm->avail_out, fp);
        assert(wlen == sizeof(out) - strm->avail_out);

        printf("  write: %zu\n", wlen);
    } while (strm->avail_out == 0); // 返回0，则表示正好out空间不够大了

    assert(strm->avail_in == 0);     /* all input will be used */
}

int file_deflate(const char *fsrc, const char *fdst)
{
    int res = -1;
    int flush = Z_NO_FLUSH;

    FILE *src = fopen(fsrc, "r");
    FILE *dst = fopen(fdst, "w+");
    assert(src && dst);

    z_stream strm = {Z_NULL};

    res = deflateInit(&strm, Z_DEFAULT_COMPRESSION);
    assert(res == Z_OK);

    /* Compress until end of file */
    do {
        char in[SIZE_CHUNK];
        size_t rlen = fread(in, 1, sizeof(in), src);
        if (rlen == 0) {
            printf("EOF\n");
            break;
        }
        // 这里需要区分文件尾或者文件中间
        flush = feof(src) ? Z_FINISH : Z_NO_FLUSH;
        __do_deflate_write(&strm, dst, in, rlen, flush);

    } while (flush != Z_FINISH);

    /* clean up and return */
    (void)deflateEnd(&strm);

    fclose(src);
    fclose(dst);
    return Z_OK;
}

static int __do_inflate_write(z_stream *strm, 
        FILE *fp, void *data, size_t size)
{
    int res = -1;
    char out[SIZE_CHUNK];

    strm->next_in = (Bytef *)data;
    strm->avail_in = size;

    printf("inflate: %u\n", strm->avail_in);

    /* run inflate() on input until output buffer not full */
    do {
        strm->avail_out = sizeof(out);
        strm->next_out  = (Bytef *)out;
        res = inflate(strm, Z_NO_FLUSH);
        assert(res != Z_STREAM_ERROR);  /* state not clobbered */
        switch (res) {
            case Z_NEED_DICT:
                res = Z_DATA_ERROR;     /* and fall through */
            case Z_DATA_ERROR:
            case Z_MEM_ERROR:
                goto out;
        }

        size_t wlen = fwrite(out, 1, sizeof(out) - strm->avail_out, fp);
        assert(wlen == sizeof(out) - strm->avail_out);

        printf("  write: %zu\n", wlen);
    } while (strm->avail_out == 0);

out:
    return res;
}


int file_inflate(const char *fsrc, const char *fdst)
{
    int res = -1;
    FILE *src = fopen(fsrc, "r");
    FILE *dst = fopen(fdst, "w+");
    assert(src && dst);

    z_stream strm = {Z_NULL};

    res = inflateInit(&strm);
    assert(res == Z_OK);

    /* decompress until deflate stream ends or end of file */
    do {
        char in[SIZE_CHUNK];
        size_t rlen = fread(in, 1, sizeof(in), src);
        if (rlen == 0) {
            printf("EOF\n");
            break;
        }

        /* done when inflate() says it's done */
        res = __do_inflate_write(&strm, dst, in, rlen);
    } while (res != Z_STREAM_END);

    /* clean up and return */
    (void)inflateEnd(&strm);
    fclose(src);
    fclose(dst);
    return res == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
}

void zilbmain(){
    file_deflate("./tmpall_dct","./tmpall_zlib");
    file_inflate("./tmpall_zlib","./tmpall_unzip");
}
